﻿using System;
using System.Data;

namespace LaoLuo.Data.LayeredFramework
{
    /// <summary>
    /// 
    /// </summary>
    public abstract class TransactionController : ITransactionController
    {
        private readonly Func<IDbConnection> _connectionFactory;

        protected TransactionController(Func<IDbConnection> connectionFactory)
        {
            _connectionFactory = connectionFactory;
        }

        /// <summary>
        /// gets the depth of current transaction
        /// </summary>
        public int TransactionDepth { get; private set; }

        /// <summary>
        /// gets a bool value that indicates whether transaction has began
        /// </summary>
        public bool IsBegin { get; private set; }

        /// <summary>
        /// gets a bool value that indicates whether transaction has commit
        /// </summary>
        public bool IsCommit { get; private set; }

        /// <summary>
        /// gets a bool value that indicates whether transaction has rollback
        /// </summary>
        public bool IsRollBack { get; private set; }

        /// <summary>
        /// gets a bool value that indicates whether current transaction is real
        /// </summary>
        public bool IsReal
        {
            get { return TransactionDepth == 1; }
        }

        /// <summary>
        /// gets the current connection. if no transaction has began, return null
        /// </summary>
        internal IDbConnection CurrentConnection { get; private set; }

        /// <summary>
        /// gets the current transaction. if no transaction has began, return null
        /// </summary>
        internal IDbTransaction CurrentTransaction { get; private set; }

        /// <summary>
        /// if there is no current transaction, make one. else increace <see cref="TransactionDepth"/>
        /// </summary>
        public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            if (CurrentTransaction == null)
            {
                CurrentTransaction = GetConnection().BeginTransaction(isolationLevel);
                IsBegin = true;
                IsRollBack = false;
                IsCommit = false;
                TransactionDepth = 0;
            }

            TransactionDepth++;
        }

        /// <summary>
        /// rollback if current transaction is a real one. else decrease <see cref="TransactionDepth"/>
        /// </summary>
        public void RollbackTransaction()
        {
            if (TransactionDepth <= 0)
            {
                throw new ApplicationException("transaction depth is " + TransactionDepth);
            }

            if (IsReal)
            {
                RealRollbackTransaction();
                return;
            }

            TransactionDepth--;
        }

        /// <summary>
        /// rollback current transaction
        /// </summary>
        public void RealRollbackTransaction()
        {
            if (CurrentTransaction == null) return;
            CurrentTransaction.Rollback();
            DisposeTransaction();
            IsRollBack = true;
            IsBegin = false;
            TransactionDepth = 0;
        }

        /// <summary>
        /// commit current transaction if it is a real one
        /// </summary>
        public void CommitTransaction()
        {
            if (TransactionDepth <= 0)
            {
                throw new ApplicationException("transaction counter is " + TransactionDepth);
            }
            if (IsReal)
            {
                CurrentTransaction.Commit();
                DisposeTransaction();
                IsCommit = true;
                IsBegin = false;
            }

            TransactionDepth--;
        }

        private IDbConnection GetConnection()
        {
            lock (_connectionFactory)
            {
                var ret = CurrentConnection ?? (CurrentConnection = _connectionFactory());

                if (ret.State == ConnectionState.Closed)
                {
                    ret.Open();
                }

                return ret;
            }
        }

        private void DisposeTransaction()
        {
            if (CurrentTransaction != null)
            {
                CurrentTransaction.Dispose();
                if (CurrentTransaction.Connection != null) CurrentTransaction.Connection.Dispose();
            }

            CurrentTransaction = null;
        }
    }
}
